﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Threading;
using System.Collections.Specialized;

namespace MVCPlugin.Utility
{
    public static class WebHelper
    {
        private static Func<string> s_ImageBaseUrlGetter = () => string.Empty;
        private static Func<string> s_WebBaseUrlGetter = () => string.Empty;

        internal static void Init(Func<string> webBaseUrlGetter, Func<string> imageBaseUrlGetter)
        {
            if (imageBaseUrlGetter != null)
            {
                s_ImageBaseUrlGetter = imageBaseUrlGetter;
            }
            if (webBaseUrlGetter != null)
            {
                s_WebBaseUrlGetter = webBaseUrlGetter;
            }
        }

        public static string WebBaseUrl
        {
            get
            {
                string baseUrl = s_WebBaseUrlGetter();
                if (string.IsNullOrWhiteSpace(baseUrl) == false)
                {
                    baseUrl = baseUrl.ToLower();
                    if (WebHelper.IsCurrentConnectionSecured() && baseUrl.StartsWith("http:", StringComparison.InvariantCultureIgnoreCase))
                    {
                        baseUrl = baseUrl.Replace("http:", "https:");
                    }
                    return baseUrl.TrimEnd('/', '\\');
                }
                string r = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + HttpContext.Current.Request.ApplicationPath;
                return r.TrimEnd('\\', '/').ToLower();
            }
        }

        public static UrlHelper CreateUrlHelper()
        {
            if (HttpContext.Current != null)
            {
                HttpContextBase contextWrapper = new HttpContextWrapper(HttpContext.Current);
                RouteData routeData = RouteTable.Routes.GetRouteData(contextWrapper);
                if (routeData != null)
                {
                    return new UrlHelper(new RequestContext(contextWrapper, routeData));
                }
            }
            return null;
        }
        
        public static string BuildFullUrl(string relativeUrlToRoot, string languageCode = null)
        {
            string r = WebBaseUrl; // HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) + HttpContext.Current.Request.ApplicationPath;
            return (r.TrimEnd('\\', '/') + "/" + (languageCode == null ? string.Empty : (languageCode + "/")) + relativeUrlToRoot.Trim('~', '\\', '/') + "/").ToLower();
        }

        public static bool IsCurrentConnectionSecured()
        {
            try
            {
                if (HttpContext.Current == null || HttpContext.Current.Request == null)
                {
                    return false;
                }
            }
            catch { return false; }
            HttpContextBase contextWrapper = new HttpContextWrapper(HttpContext.Current);
            return (contextWrapper.Request.IsSecureConnection
                || contextWrapper.Request.Url.Scheme.ToLower() == "https"
                || contextWrapper.Request.ServerVariables["HTTP_CLUSTER_HTTPS"] != null
                || contextWrapper.Request.ServerVariables["HTTP_CLUSTER_HTTPS"] == "on");
        }

        private static AspNetHostingPermissionLevel? s_TrustLevel = null;
        public static AspNetHostingPermissionLevel GetTrustLevel()
        {
            if (!s_TrustLevel.HasValue)
            {
                //set minimum
                s_TrustLevel = AspNetHostingPermissionLevel.None;

                //determine maximum
                foreach (AspNetHostingPermissionLevel trustLevel in
                        new AspNetHostingPermissionLevel[] {
                                AspNetHostingPermissionLevel.Unrestricted,
                                AspNetHostingPermissionLevel.High,
                                AspNetHostingPermissionLevel.Medium,
                                AspNetHostingPermissionLevel.Low,
                                AspNetHostingPermissionLevel.Minimal 
                            })
                {
                    try
                    {
                        new AspNetHostingPermission(trustLevel).Demand();
                        s_TrustLevel = trustLevel;
                        break; //we've set the highest permission we can
                    }
                    catch (System.Security.SecurityException)
                    {
                        continue;
                    }
                }
            }
            return s_TrustLevel.Value;
        }

        private static bool TryWriteWebConfig()
        {
            try
            {
                string f = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "web.config");
                // In medium trust, "UnloadAppDomain" is not supported. Touch web.config
                // to force an AppDomain restart.
                File.SetLastWriteTimeUtc(f, DateTime.UtcNow);
                return true;
            }
            catch
            {
                return false;
            }
        }

        private static bool TryWriteGlobalAsax()
        {
            try
            {
                string f = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "global.asax");
                //When a new plugin is dropped in the Plugins folder and is installed into Application, 
                //even if the plugin has registered routes for its controllers, 
                //these routes will not be working as the MVC framework couldn't 
                //find the new controller types and couldn't instantiate the requested controller. 
                //That's why you get these nasty errors 
                //i.e "Controller does not implement IController".
                //The solutino is to touch global.asax file
                File.SetLastWriteTimeUtc(f, DateTime.UtcNow);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static void RestartAppDomain(int delayMilliseconds)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(x =>
            {
                Thread.Sleep(delayMilliseconds);
                WebHelper.RestartAppDomain();
            }));
        }

        public static void RestartAppDomain(bool makeRedirect = false, string redirectUrl = "")
        {
            if (GetTrustLevel() > AspNetHostingPermissionLevel.Medium)
            {
                //full trust
                HttpRuntime.UnloadAppDomain();

                TryWriteGlobalAsax();
            }
            else
            {
                //medium trust
                bool success = TryWriteWebConfig();
                if (!success)
                {
                    throw new ApplicationException("The application needs to be restarted due to a configuration change, but was unable to do so." + Environment.NewLine +
                        "To prevent this issue in the future, a change to the web server configuration is required:" + Environment.NewLine +
                        "- run the application in a full trust environment, or" + Environment.NewLine +
                        "- give the application write access to the 'web.config' file.");
                }

                success = TryWriteGlobalAsax();
                if (!success)
                {
                    throw new ApplicationException("The application needs to be restarted due to a configuration change, but was unable to do so." + Environment.NewLine +
                        "To prevent this issue in the future, a change to the web server configuration is required:" + Environment.NewLine +
                        "- run the application in a full trust environment, or" + Environment.NewLine +
                        "- give the application write access to the 'Global.asax' file.");
                }
            }

            // If setting up extensions/modules requires an AppDomain restart, it's very unlikely the
            // current request can be processed correctly.  So, we redirect to the same URL, so that the
            // new request will come to the newly started AppDomain.
            if (HttpContext.Current != null && makeRedirect)
            {
                if (String.IsNullOrEmpty(redirectUrl))
                {
                    redirectUrl = HttpContext.Current.Request.Url.AbsoluteUri;
                }
                HttpContext.Current.Response.Redirect(redirectUrl, true);
            }
        }

        public static string GetStaticResourcePath(string relativePath)
        {
            if (relativePath == null)
            {
                relativePath = string.Empty;
            }
            else
            {
                relativePath = relativePath.Trim().ToLower();
            }
            string baseUrl = s_ImageBaseUrlGetter();
            if (string.IsNullOrWhiteSpace(baseUrl))
            {
                if (relativePath.StartsWith("/") == false)
                {
                    return "/" + relativePath;
                }
                return relativePath;
            }
            baseUrl = baseUrl.ToLower();
            if (WebHelper.IsCurrentConnectionSecured() && baseUrl.StartsWith("http:"))
            {
                baseUrl = baseUrl.Replace("http:", "https:");
            }
            return (baseUrl.TrimEnd('/', '\\') + "/" + relativePath.TrimStart('/', '\\')).ToLower();
        }

        public static string GetUploadedImagePath(string relativePath, int width)
        {
            return GetUploadedImagePath(relativePath, width, width);
        }

        public static string GetUploadedImagePath(string relativePath, int width, int height)
        {
            return GetUploadedImagePath(width + "/" + height + "/" + relativePath.Trim().TrimStart('/', '\\').Replace('\\', '/'));
        }

        public static string GetUploadedImagePath(string relativePath)
        {
            relativePath = "/" + relativePath.Trim().TrimStart('/', '\\').Replace('\\', '/');
            //string t = ImageUploadedFolderPath;
            //if (relativePath.ToLower().StartsWith(t.ToLower()) == false)
            //{
            //    relativePath = t + relativePath;
            //}
            return GetStaticResourcePath(relativePath);
        }

        public static string GetClientIP()
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return string.Empty;
            }
            string result = String.Empty;
            result = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (null == result || result.Trim() == String.Empty)
            {
                result = context.Request.ServerVariables["REMOTE_ADDR"];
            }
            if (null == result || result.Trim() == String.Empty)
            {
                result = context.Request.UserHostAddress;
            }
            if (null == result || result.Trim() == String.Empty)
            {
                return "Unknown";
            }
            return result;
        }
        
        public static bool HasUrlQueryStringParamValue(string paramName)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return false;
            }
            return context.Request.QueryString[paramName] != null
                && context.Request.QueryString[paramName].Trim().Length > 0;
        }

        public static string GetUrlQueryStringParamValue(string paramName)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return string.Empty;
            }
            if (HasUrlQueryStringParamValue(paramName))
            {
                return context.Request.QueryString[paramName].Trim();
            }
            return string.Empty;
        }

        public static bool HasPostFormParamValue(string paramName)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return false;
            }
            return context.Request.Form[paramName] != null && context.Request.Form[paramName].Trim().Length > 0;
        }

        public static string GetPostFormParamValue(string paramName)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return string.Empty;
            }
            if (HasPostFormParamValue(paramName))
            {
                return context.Request.Form[paramName].Trim();
            }
            return string.Empty;
        }

        public static string GetCurrentQueryStringByAppendParam(string paramName, string paramValue)
        {
            return GetCurrentQueryStringByAppendParam(true, paramName, paramValue);
        }

        public static string GetCurrentQueryStringByAppendParam(bool needAnchor, string paramName, string paramValue)
        {
            return GetCurrentQueryStringByAppendParam(needAnchor, new Dictionary<string, string>(2) { { paramName, paramValue } });
        }

        public static string GetCurrentQueryStringByAppendParam(Dictionary<string, string> parameters)
        {
            return GetCurrentQueryStringByAppendParam(true, parameters);
        }

        public static string GetCurrentQueryStringByAppendParam(bool needAnchor, Dictionary<string, string> parameters)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return string.Empty;
            }
            List<string> keys = new List<string>(parameters.Keys);
            NameValueCollection collection = context.Request.QueryString;
            StringBuilder sb = new StringBuilder();
            foreach (string entry in collection.AllKeys)
            {
                if (!keys.Exists(s => s.ToUpper() == entry.ToUpper()))
                {
                    if (sb.Length > 0)
                    {
                        sb.Append("&");
                    }
                    sb.AppendFormat("{0}={1}", entry, context.Server.UrlEncode(collection[entry]));
                }
            }
            Dictionary<string, string>.Enumerator iterator = parameters.GetEnumerator();
            while (iterator.MoveNext())
            {
                if (sb.Length > 0)
                {
                    sb.Append("&");
                }
                sb.AppendFormat("{0}={1}", iterator.Current.Key, context.Server.UrlEncode(iterator.Current.Value));
            }
            if (needAnchor && context.Request.RawUrl.Contains('#'))
            {
                sb.AppendFormat("#{0}", context.Request.RawUrl.Substring(context.Request.RawUrl.IndexOf('#')));
            }
            return sb.ToString();
        }

        public static string GetCurrentQueryStringByRemoveParam(params string[] paramNames)
        {
            return GetCurrentQueryStringByRemoveParam(true, paramNames);
        }

        public static string GetCurrentQueryStringByRemoveParam(bool needAnchor, params string[] paramNames)
        {
            HttpContext context = HttpContext.Current;
            if (context == null)
            {
                return string.Empty;
            }
            NameValueCollection collection = context.Request.QueryString;
            StringBuilder sb = new StringBuilder();
            List<string> keyList = new List<string>(paramNames);
            foreach (string entry in collection.AllKeys)
            {
                if (!keyList.Exists(s => s.ToUpper() == entry.ToUpper()))
                {
                    if (sb.Length > 0)
                    {
                        sb.Append("&");
                    }
                    sb.AppendFormat("{0}={1}", entry, context.Server.UrlEncode(collection[entry]));
                }
            }
            if (needAnchor && context.Request.RawUrl.Contains('#'))
            {
                sb.AppendFormat("#{0}", context.Request.RawUrl.Substring(context.Request.RawUrl.IndexOf('#')));
            }
            return sb.ToString();
        }



        public static string AddQueryStringParameter(string filterName, string filterValue, bool allowMultipleValues = false)
        {
            if (string.IsNullOrEmpty(filterName) || string.IsNullOrEmpty(filterValue))
            {
                return string.Empty;
            }

            string url = HttpContext.Current.Request.Url.AbsoluteUri;
            if (url.Contains("?"))
            {
                url = url.Substring(0, url.IndexOf("?"));
            }
            url = url + "?";

            filterName = filterName.Trim().ToLower();
            filterValue = filterValue.Trim().ToLower();

            bool filterNameInQuery = false;
            string filterValueInQuery = "";

            var queryString = HttpContext.Current.Request.QueryString;
            foreach (string k in queryString.AllKeys)
            {
                string key = k;
                string value = queryString.Get(key);
                if (string.IsNullOrEmpty(value)) { continue; }

                key = key.Trim().ToLower();
                value = value.Trim().ToLower();

                if (key == filterName)
                {
                    filterNameInQuery = true;
                    filterValueInQuery = value;
                }
                else
                {
                    if (url.EndsWith("?"))
                    {
                        url = url + string.Format("{0}={1}", key, value);
                    }
                    else
                    {
                        url = url + string.Format("&{0}={1}", key, value);
                    }
                }
            }

            if (filterNameInQuery)
            {
                List<string> values = filterValueInQuery.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
                if (allowMultipleValues)
                {
                    if (values.Contains(filterValue))
                    {
                        values.Remove(filterValue);
                    }
                    else
                    {
                        values.Add(filterValue);
                    }
                    filterValue = string.Join(",", values.ToArray());
                }
                else
                {
                    if (values.Contains(filterValue))
                    {
                        filterValue = "";
                    }
                }
            }
            if (!string.IsNullOrEmpty(filterValue))
            {
                if (url.EndsWith("?"))
                {
                    url = url + string.Format("{0}={1}", filterName, filterValue);
                }
                else
                {
                    url = url + string.Format("&{0}={1}", filterName, filterValue);
                }
            }

            if (url.EndsWith("?"))
            {
                url = url.Substring(0, url.Length - 1);
            }
            return url;
        }

        public static IDictionary<Guid, IList<Guid>> GetAttrbuteFilters()
        {
            IDictionary<Guid, IList<Guid>> dic = new Dictionary<Guid, IList<Guid>>();

            var queryString = HttpContext.Current.Request.QueryString;
            foreach (string key in queryString.AllKeys)
            {
                if (key.StartsWith("attr_"))
                {
                    string value = queryString.Get(key);
                    string k = key.Replace("attr_", "").Trim();
                    Guid tmp_Key;
                    if (!string.IsNullOrEmpty(value) && Guid.TryParse(k, out tmp_Key) && tmp_Key != Guid.Empty)
                    {
                        //Guid tmp_Value;
                        value = value.Trim().ToLower();
                        string[] parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                        List<Guid> valueList = new List<Guid>(parts.Length);
                        foreach (var p in parts)
                        {
                            Guid tmp_Value;
                            if (!string.IsNullOrEmpty(p) && Guid.TryParse(p, out tmp_Value) && tmp_Value != Guid.Empty)
                            {
                                valueList.Add(tmp_Value);
                            }
                        }
                        if (valueList.Count > 0)
                        {
                            dic.Add(tmp_Key, valueList);
                        }
                    }
                }
            }
            return dic;
        }
    }
}
